package org.chemodansama.fifteen;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;

import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11ExtensionPack;

import org.chemodansama.engine.LogTag;
import org.chemodansama.engine.math.NpVec3;

import android.util.Log;

final class NormalisationCubeMap {
    
    private int mTexID = 0;

    public NormalisationCubeMap(GL10 gl) {
        ByteBuffer bb = ByteBuffer.allocateDirect(4);
        bb.order(ByteOrder.nativeOrder());
        IntBuffer b = bb.asIntBuffer();
        
        gl.glGenTextures(1, b);
        
        mTexID = b.get(0);
        
        gl.glBindTexture(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, mTexID);
        
        gl.glTexParameterx(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, 
                           GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
        
        gl.glTexParameterx(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, 
                           GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);

        if (gl.glGetError() != GL10.GL_NO_ERROR) {
            if (mTexID != 0) {
                gl.glDeleteTextures(1, b);
            }
            
            Log.e(LogTag.TAG, "cubemap bound failed");
        } else {
            if (Log.isLoggable(LogTag.TAG, Log.INFO)) {
                Log.i(LogTag.TAG, "cubemap bound ok");
            }
        }
        
        int size = 64;
        
        ByteBuffer data = ByteBuffer.allocateDirect(size * size * 3);
        data.order(ByteOrder.nativeOrder());
        
        byte[] pixels = new byte[size * size * 3];
        
        int halfSize = size / 2;
        float offset = 0.5f;

        float[] v = new float[3];
        
        // Positive X
        int pixInd = 0;
        
        for (int j = -halfSize; j < halfSize; j++) {
            for (int i = -halfSize; i < halfSize; i++) {
                v[0] = halfSize;
                v[1] = -(j + offset);
                v[2] = -(i + offset);
                
                NpVec3.normalize(v);
                NpVec3.packTo01(v);

                pixels[pixInd++] = (byte) (v[0] * 255);
                pixels[pixInd++] = (byte) (v[1] * 255);
                pixels[pixInd++] = (byte) (v[2] * 255);
            }
        }

        data.put(pixels);
        data.rewind();
        
        gl.glTexImage2D(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_POSITIVE_X, 
                        0, GL10.GL_RGB, size, size, 0, 
                        GL10.GL_RGB, GL10.GL_UNSIGNED_BYTE, data);
        
        // Negative X
        pixInd = 0;
        
        for (int j = -halfSize; j < halfSize; j++) {
            for (int i = -halfSize; i < halfSize; i++) {
                v[0] = -halfSize;
                v[1] = -(j + offset);
                v[2] = (i + offset);
                
                NpVec3.normalize(v);
                NpVec3.packTo01(v);

                pixels[pixInd++] = (byte) (v[0] * 255);
                pixels[pixInd++] = (byte) (v[1] * 255);
                pixels[pixInd++] = (byte) (v[2] * 255);
            }
        }
        
        data.put(pixels);
        data.rewind();
        
        gl.glTexImage2D(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 
                        0, GL10.GL_RGB, size, size, 0, 
                        GL10.GL_RGB, GL10.GL_UNSIGNED_BYTE, data);
     
        
        // Positive Y
        pixInd = 0;
        
        for (int j = -halfSize; j < halfSize; j++) {
            for (int i = -halfSize; i < halfSize; i++) {
                v[0] = i + offset;
                v[1] = halfSize;
                v[2] = j + offset;
                
                NpVec3.normalize(v);
                NpVec3.packTo01(v);

                pixels[pixInd++] = (byte) (v[0] * 255);
                pixels[pixInd++] = (byte) (v[1] * 255);
                pixels[pixInd++] = (byte) (v[2] * 255);
            }
        }
        
        data.put(pixels);
        data.rewind();
        
        gl.glTexImage2D(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 
                        0, GL10.GL_RGB, size, size, 0, 
                        GL10.GL_RGB, GL10.GL_UNSIGNED_BYTE, data);
        
        
        // Negative Y
        pixInd = 0;
        
        for (int j = -halfSize; j < halfSize; j++) {
            for (int i = -halfSize; i < halfSize; i++) {
                v[0] = i + offset;
                v[1] = -halfSize;
                v[2] = -(j + offset);
                
                NpVec3.normalize(v);
                NpVec3.packTo01(v);

                pixels[pixInd++] = (byte) (v[0] * 255);
                pixels[pixInd++] = (byte) (v[1] * 255);
                pixels[pixInd++] = (byte) (v[2] * 255);
            }
        }
        
        data.put(pixels);
        data.rewind();
        
        gl.glTexImage2D(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 
                        0, GL10.GL_RGB, size, size, 0, 
                        GL10.GL_RGB, GL10.GL_UNSIGNED_BYTE, data);
        
        // Positive Z
        pixInd = 0;
        
        for (int j = -halfSize; j < halfSize; j++) {
            for (int i = -halfSize; i < halfSize; i++) {
                v[0] = i + offset;
                v[1] = -(j + offset);
                v[2] = halfSize;
                
                NpVec3.normalize(v);
                NpVec3.packTo01(v);

                pixels[pixInd++] = (byte) (v[0] * 255);
                pixels[pixInd++] = (byte) (v[1] * 255);
                pixels[pixInd++] = (byte) (v[2] * 255);
            }
        }
        
        data.put(pixels);
        data.rewind();
        
        gl.glTexImage2D(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 
                        0, GL10.GL_RGB, size, size, 0, 
                        GL10.GL_RGB, GL10.GL_UNSIGNED_BYTE, data);
        
        
        // Negative Z
        pixInd = 0;
        
        for (int j = -halfSize; j < halfSize; j++) {
            for (int i = -halfSize; i < halfSize; i++) {
                v[0] = -(i + offset);
                v[1] = -(j + offset);
                v[2] = -halfSize;
                
                NpVec3.normalize(v);
                NpVec3.packTo01(v);

                pixels[pixInd++] = (byte) (v[0] * 255);
                pixels[pixInd++] = (byte) (v[1] * 255);
                pixels[pixInd++] = (byte) (v[2] * 255);
            }
        }
        
        data.put(pixels);
        data.rewind();
        
        gl.glTexImage2D(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 
                        0, GL10.GL_RGB, size, size, 0, 
                        GL10.GL_RGB, GL10.GL_UNSIGNED_BYTE, data);
        
        if (gl.glGetError() == GL10.GL_NO_ERROR) {
            Log.i(LogTag.TAG, "cubemap created ok");
        } else {
            Log.e(LogTag.TAG, "cubemap creation failed");
        }
    }
    
    public void releaseAssets(GL10 gl) {
        ByteBuffer bb = ByteBuffer.allocateDirect(4);
        bb.order(ByteOrder.nativeOrder());
        IntBuffer b = bb.asIntBuffer();
        
        b.put(mTexID);
        b.rewind();
        
        gl.glDeleteTextures(1, b);
        
        mTexID = 0;
    }
    
    public void bind(GL10 gl) {
        if (mTexID != 0) {
            gl.glBindTexture(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, mTexID);
        }
    }
}